﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace CodeContrib
{
	public static class CollectionExtensions
	{
		private const string _defaultDeliminator = "&";

		[DebuggerStepThrough]
		public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
		{
			foreach (var item in items)
			{
				collection.Add(item);
			}
		}

		[DebuggerStepThrough]
		public static bool Any(this Array collection)
		{
			return (collection.Length > 0);
		}

		[DebuggerStepThrough]
		public static bool Any<T>(this ICollection<T> collection)
		{
			return (collection.Count > 0);
		}

		[DebuggerStepThrough]
		public static bool Any(this NameValueCollection collection)
		{
			return (collection.AllKeys.Length > 0);
		}

		[DebuggerStepThrough]
		public static IEnumerable<T> AsNullIfEmpty<T>(this IEnumerable<T> collection)
		{
			if (collection == null
			    || !collection.Any())
			{
				return null;
			}

			return collection;
		}

		[DebuggerStepThrough]
		public static bool Contains(this Array collection, object value)
		{
			return collection.Cast<object>().Contains(value);
		}

		[DebuggerStepThrough]
		public static bool ContainsKey(this NameValueCollection collection, string key)
		{
			return (collection.AllKeys.Contains(key));
		}

		[DebuggerStepThrough]
		public static NameValueCollection Copy(this NameValueCollection collection)
		{
			var destination = new NameValueCollection();
			destination.Merge(collection, true);

			return destination;
		}

		[DebuggerStepThrough]
		public static T FirstOrDefault<T>(this IEnumerable<T> collection, T defaultValue)
		{
			if (!collection.Any())
			{
				return defaultValue;
			}

			return collection.First();
		}

		public static T FirstOrDefault<T>(this IEnumerable<T> collection, Func<T, bool> predicate, T defaultValue)
		{
			if (!collection.Any(predicate))
			{
				return defaultValue;
			}

			return collection.First(predicate);
		}

		[DebuggerStepThrough]
		public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
		{
			foreach (var item in collection)
			{
				action(item);
			}
		}

		[DebuggerStepThrough]
		public static string[] Get(this NameValueCollection collection, string key)
		{
			var values = collection[key];

			return (values != null) ? values.Split(',') : new string[0];
		}

		[DebuggerStepThrough]
		public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> collection, TKey key)
		{
			TValue value;

			collection.TryGetValue(key, out value);

			return value;
		}

		[DebuggerStepThrough]
		public static int IndexOf<T>(this IEnumerable<T> collection, T value) where T : IEquatable<T>
		{
			return collection.IndexOf(value, EqualityComparer<T>.Default);
		}

		[DebuggerStepThrough]
		public static int IndexOf<T>(this IEnumerable<T> collection, T value, IEqualityComparer<T> comparer)
		{
			var index = 0;

			foreach (var item in collection)
			{
				if (comparer.Equals(item, value))
				{
					return index;
				}

				index++;
			}

			return -1;
		}

		[DebuggerStepThrough]
		public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection)
		{
			return (collection == null || !collection.Any());
		}

		[DebuggerStepThrough]
		public static string Join(this NameValueCollection collection)
		{
			return Join(collection, _defaultDeliminator);
		}

		[DebuggerStepThrough]
		public static string Join(this NameValueCollection collection, string deliminator)
		{
			Guard.NotNullOrEmpty(deliminator, "deliminator");

			var result = new StringBuilder();

			foreach (string key in collection.Keys)
			{
				if (result.Length > 0)
				{
					result.Append(deliminator);
				}

				result.Append(string.Format("{0}={1}", key, collection[key]));
			}

			return result.ToString();
		}

		[DebuggerStepThrough]
		public static T LastOrDefault<T>(this IEnumerable<T> collection, T defaultValue)
		{
			if (!collection.Any())
			{
				return defaultValue;
			}

			return collection.Last();
		}

		[DebuggerStepThrough]
		public static T LastOrDefault<T>(this IEnumerable<T> collection, Func<T, bool> predicate, T defaultValue)
		{
			if (!collection.Any(predicate))
			{
				return defaultValue;
			}

			return collection.Last(predicate);
		}

		[DebuggerStepThrough]
		public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> collection, IDictionary<TKey, TValue> explicitCollection, bool replaceExisting) where TKey : class where TValue : class
		{
			if (explicitCollection == null)
			{
				return;
			}

			foreach (var item in explicitCollection)
			{
				collection.Merge(item.Key, item.Value, replaceExisting);
			}
		}

		[DebuggerStepThrough]
		public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> collection, TKey key, TValue value, bool replaceExisting) where TKey : class where TValue : class
		{
			Guard.NotNull(key, "key");

			if (!replaceExisting
			    && collection.ContainsKey(key))
			{
				return;
			}

			if (value == null)
			{
				collection.Remove(key);
			}
			else
			{
				collection[key] = value;
			}
		}

		[DebuggerStepThrough]
		public static void Merge(this NameValueCollection collection, NameValueCollection explicitCollection, bool replaceExisting)
		{
			if (explicitCollection == null)
			{
				return;
			}

			foreach (var key in explicitCollection.AllKeys)
			{
				collection.Merge(key, explicitCollection[key], replaceExisting);
			}
		}

		[DebuggerStepThrough]
		public static void Merge(this NameValueCollection collection, string key, string value, bool replaceExisting)
		{
			Guard.NotNull(key, "key");

			if (!replaceExisting
			    && collection.ContainsKey(key))
			{
				return;
			}

			if (value == null)
			{
				collection.Remove(key);
			}
			else
			{
				collection[key] = value;
			}
		}

		[DebuggerStepThrough]
		public static void Remove<TKey, TValue>(this IDictionary<TKey, TValue> collection, IEnumerable<TKey> keys)
		{
			foreach (var key in keys)
			{
				collection.Remove(key);
			}
		}

		[DebuggerStepThrough]
		public static void RemoveInverted<TKey, TValue>(this IDictionary<TKey, TValue> collection, IEnumerable<TKey> keys)
		{
			foreach (var key in collection.Keys.Where(x => !keys.Contains(x)))
			{
				collection.Remove(key);
			}
		}

		[DebuggerStepThrough]
		public static IList<T> Shuffle<T>(this IList<T> collection)
		{
			var i = collection.Count();

			while (i > 1)
			{
				i--;

				var j = new RandomInteger(i + 1).Next();

				var obj = collection[j];

				collection[j] = collection[i];
				collection[i] = obj;
			}

			return collection;
		}

		[DebuggerStepThrough]
		public static T SingleOrDefault<T>(this IEnumerable<T> collection, T defaultValue)
		{
			if (!collection.Any())
			{
				return defaultValue;
			}

			return collection.Single();
		}

		[DebuggerStepThrough]
		public static T SingleOrDefault<T>(this IEnumerable<T> collection, Func<T, bool> predicate, T defaultValue)
		{
			if (!collection.Any(predicate))
			{
				return defaultValue;
			}

			return collection.Single(predicate);
		}

		[DebuggerStepThrough]
		public static IDictionary<string, object> ToDictionary(this object keyValuePairs)
		{
			if (keyValuePairs is IDictionary<string, object>)
			{
				return (IDictionary<string, object>)keyValuePairs;
			}

			var dictionary = new Dictionary<string, object>(StringComparer.Ordinal);

			if (keyValuePairs != null)
			{
				foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(keyValuePairs))
				{
					var value = property.GetValue(keyValuePairs);

					dictionary[property.Name] = value;
				}
			}

			return dictionary;
		}

		[DebuggerStepThrough]
		public static NameValueCollection ToNameValueCollection(this IEnumerable<string> collection)
		{
			var nameValueCollection = new NameValueCollection();

			var count = collection.Count();

			if (count >= 2)
			{
				for (var i = 0; i < count; i++)
				{
					if (i == 0
					    || !i.IsOdd())
					{
						continue;
					}

					var key = collection.ElementAt(i - 1);
					var value = collection.ElementAt(i);

					nameValueCollection.Add(key, value);
				}
			}

			return nameValueCollection;
		}

		[DebuggerStepThrough]
		public static NameValueCollection ToNameValueCollection(this IEnumerable<string> keys, IEnumerable<string> values)
		{
			var nameValueCollection = new NameValueCollection();

			for (var i = 0; i < keys.Count(); i++)
			{
				if (i < values.Count())
				{
					nameValueCollection.Add(keys.ElementAt(i), values.ElementAt(i));
				}
			}

			return nameValueCollection;
		}
	}
}